/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */
#pragma once

#include <Converter.h>
#include "vector2.h"
#include "utl.h"
#include <AxisMode.h>
#include <system/assert.h>
#include <math/Matrix4.h>

namespace liba {
namespace math {

template< class T >
class Rect
{
public:
	T x, y, width, height;

	Rect()
	{}

	Rect( const T & x, const T & y, const T & width, const T & height )
	:	x( x ),
		y( y ),
		width( width ),
		height( height )
	{}

	Vector2< T > get_coord()const
	{
		return Vector2<T>( x, y );
	}

	T get_right()const
	{
		return x + width;
	}

	/** Small amount of template magic to simplify living with different y axis orientations. Defaults is windows-like y axis down. */
	template<YAxisMode mode>
	T get_bottom()const
	{
		if (mode == Y_AXIS_DOWN)
			return y + height;
		else if (mode == Y_AXIS_UP)
			return y;
		else
			LIBA_FAIL_UNEXPECTED();
	}

	T get_bottom()const
	{
		return get_bottom<Y_AXIS_DOWN>();
	}

	template<YAxisMode mode>
	T get_top() const
	{
		if (mode == Y_AXIS_DOWN)
			return y;
		else if (mode == Y_AXIS_UP)
			return y + height;
		else
			LIBA_FAIL_UNEXPECTED();
	}

	T get_top() const
	{
		return get_top<Y_AXIS_DOWN>();
	}

	bool is_intersects( const Rect<T> & r )const
	{
		T dx = math::abs( x + width/T(2) - ( r.x + r.width/T(2) ) ); //distance between centers (X axis)
		if( dx < (width + r.width)/T(2) )
		{
			T dy = math::abs( y + height/T(2) - ( r.y + r.height/T(2) ) );
			if( dy < (height + r.height)/T(2) )
				return true;
		}
		return false;
	}

	template< class V >
	bool is_intersects( const Vector2<V> & p )const
	{
		if( math::abs( x + width/T(2) - p.x ) < width/T(2) )
			if( math::abs( y + height/T(2) - p.y ) < height/T(2) )
				return true;

		return false;
	}

	template< class V >
	bool is_intersects( const V & px, const V & py )const
	{
		if( math::abs( x + width/T(2) - px ) < width/T(2) )
			if( math::abs( y + height/T(2) - py ) < height/T(2) )
				return true;

		return false;
	}

	bool intersect( const Rect<T>& r )
	{
		intersect_segment( x, width, r.x, r.width );
		intersect_segment( y, height, r.y, r.height );
		return !is_empty();
	}

	bool is_empty()const
	{
		return width == T(0) || height == T(0);
	}

	template<typename Sym>
	struct ConverterBuffer
		:	public converter::ConverterStaticBuffer<Sym, 28*4>
	{
		bool convert(const Rect<T> & value)
		{
			T x = value.x;
			T y = value.y;
			T width = value.width;
			T height = value.height;
			this->size = converter::simple_print( this->data, x );
			this->data[this->size++] = ' ';
			this->size += converter::simple_print( this->data + this->size, y );
			this->data[this->size++] = ' ';
			this->size += converter::simple_print( this->data + this->size, width );
			this->data[this->size++] = ' ';
			this->size += converter::simple_print( this->data + this->size, height );
			return true;
		}
	};

	template<class Sym>
	bool convert(const Sym * be, const Sym * en)
	{
		T x = 0;
		T y = 0;
		T width = 0;
		T height = 0;
		const Sym * beg = be;
		beg = converter::simple_convert( beg, en, &x);
		if( !beg )
			return false;
		beg = converter::simple_convert( beg, en, &y);
		if( !beg )
			return false;
		beg = converter::simple_convert( beg, en, &width);
		if( !beg )
			return false;
		beg = converter::simple_convert( beg, en, &height);
		this->x = x;
		this->y = y;
		this->width = width;
		this->height = height;
		return true;
	}

	void shift( const Vector2<T> & vec )
	{
		x += vec.x;
		y += vec.y;
	}

	bool operator == ( const Rect<T> & other )const
	{
		return x == other.x && y == other.y && width == other.width && height == other.height;
	}
	bool operator != ( const Rect<T> & other )const
	{
		return !operator==( other );
	}

private:
	static void intersect_segment( T & x1, T & width1, T x2, T width2 )
	{
		if( x1 > x2 )
		{
			std::swap<T>( x1, x2 );
			std::swap<T>( width1, width2 );
		}

		if( x1 + width1 > x2 )
		{
			width1 = x1 + width1 - x2;
			x1 = x2;
			if( width1 > width2 )
				width1 = width2;
		}
		else
			width1 = T(0);
	}
};

/** Transforms given AABB as if it had given z value with the given matrix. */
math::Rect<float> Transform(const math::Rect<float>& src, float z, const Matrix4<float>& m);

} //namespace math
} //namespace liba

using namespace liba;